﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Fuse8.DomainFramework.Common;

using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.SystemFacade.Extensions;
using Fuse8.ContentManagementFramework.SystemFacade.Utilities;

namespace Fuse8.ContentManagementFramework.Web.MvcPowered
{
    [Serializable]
    public class CmsMvcPartialModel
    {   
        public string PartialViewName { get; set; }

        public string PartialAdministrationViewName { get; set; }

        public string ActionController { get; set; }

        public string ActionName { get; set; }

        public string PartialModelType { get; set; }

        public string Caption { get; set; }
    }

    [Serializable]
    public class CmsMvcPartialAdministrationModel : CmsMvcPartialModel
    {
        public int PageControlId { get; set; }
        
        public object PartialViewModel { get; set; }

        public int? ModuleId { get; set; }

        public string ModuleName { get; set; }

        public string ModuleDescription { get; set; }
    }

    public class PageControlModelTypeParameter
    {
        public object Value { get; set; }
        public string Name { get; set; }
        public int Id { get; set; }
        public bool IsSimple { get; set; }
        public string Editor { get; set; }
        public object AdditionalValues { get; set; }
    }

    public static class PageControlExtensions
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="control"></param>
        /// <returns>PartialViewName, PartialAdministrationViewName, PartialViewModel, PartialModelType, ActionController, ActionName</returns>
        public static dynamic GetCmsMvcPartialControlParameters(this CmsPageControl control)
        {
            Guard.ArgumentNotNull(control, "control");

            dynamic result = new ExpandoObject();

            result.PartialViewName = string.Empty;
            result.PartialAdministrationViewName = string.Empty;
            result.PartialViewModel = null;
            result.PartialModelType = null;
            result.ActionController = null;
            result.ActionName = null;
            result.ModuleId = null;
            result.ModuleName = null;

            CmsMvcPartialModel partialModel = null;

            if (!string.IsNullOrWhiteSpace(control.PageControlViewValue))
            {
                partialModel = control.PageControlViewValue.DigUpFromString<CmsMvcPartialModel>();
            }
            else if (control.Module != null)
            {
                var module = control.Module;

                partialModel = module.ModuleViewParameters.DigUpFromString<CmsMvcPartialModel>();

                result.ModuleId = control.ModuleId;
                result.ModuleName = module.ModuleName;
                result.ModuleDescription = module.ModuleDescription;
            }

            if (partialModel != null)
            {
                result.PartialViewName = partialModel.PartialViewName;
                result.PartialAdministrationViewName = partialModel.PartialAdministrationViewName;
                result.ActionController = partialModel.ActionController;
                result.ActionName = partialModel.ActionName;

                if (!string.IsNullOrWhiteSpace(partialModel.PartialModelType))
                {
                    result.PartialModelType = partialModel.PartialModelType;

                    var modelType = Type.GetType(partialModel.PartialModelType, true, true);
                    result.PartialViewModel = !string.IsNullOrWhiteSpace(control.PageControlModelValue) ?
                        control.PageControlModelValue.DigUpFromString(modelType) :
                        Activator.CreateInstance(modelType);
                }
            }

            return result;
        }

        public static void SaveCmsModuleParameters(this CmsPageControl control, HttpContextBase httpContext, NameValueCollection elements)
        {
            Guard.ArgumentNotNull(control, "control");
            Guard.ArgumentNotNull(httpContext, "httpContext");
            Guard.ArgumentNotNull(elements, "elements");

            if (control.ModuleId.HasValue)
            {
                var module = control.Module;

                var partialModel = module.ModuleViewParameters.DigUpFromString<CmsMvcPartialModel>();

                if (partialModel != null)
                {
                    SaveCmsMvcPartialModel(httpContext, control, partialModel, elements);
                }
            }
        }

        public static void SaveCmsMvcPartialControlParameters(this CmsPageControl control, HttpContextBase httpContext, NameValueCollection elements)
        {
            Guard.ArgumentNotNull(control, "control");
            Guard.ArgumentNotNull(elements, "elements");

            if (!string.IsNullOrWhiteSpace(control.PageControlViewValue))
            {
                var partialModel = control.PageControlViewValue.DigUpFromString<CmsMvcPartialModel>();
                if (partialModel != null)
                {
                    SaveCmsMvcPartialModel(httpContext, control, partialModel, elements);
                }
            }
        }

        public static IList<PageControlModelTypeParameter> GetPartialModelTypeParameters(this CmsMvcPartialAdministrationModel model)
        {
            var type = Type.GetType(model.PartialModelType, true, true);

            var result = new List<PageControlModelTypeParameter>();

            if (type.IsEnum || type.IsValueType || type.Equals(typeof(string)))
            {
                var parameter = new PageControlModelTypeParameter()
                                    {
                                        IsSimple = true,
                                        Name = "",
                                        Value = model.PartialViewModel,
                                        Id = model.PageControlId
                                    };

                SetEditor(parameter, type);

                result.Add(parameter);
            }
            else
            {
                foreach (var property in type.GetProperties())
                {
                    var parameter = new PageControlModelTypeParameter()
                                        {
                                            IsSimple = false,
                                            Name = property.Name,
                                            Value = property.GetValue(model.PartialViewModel, null),
                                            Id = model.PageControlId
                                        };
                    SetEditor(parameter, property.PropertyType);
                    result.Add(parameter);
                }
            }

            return result;
        }

        private static void SetEditor(PageControlModelTypeParameter parameter, Type type)
        {            
            if (type.IsEnum)
            {
                parameter.Editor = "SingleSelect";
                parameter.AdditionalValues = new
                                                 {
                                                     DataSource =
                                                         new SelectList(
                                                             Enum.GetNames(type).Select(p => new {Key = p, Value = p}),
                                                             "Value", "Key", parameter.Value)
                                                 };
            }
            else if(type.Equals(typeof(bool)))
            {
                parameter.Editor = "Checkbox";
            }
            else
            {
                parameter.Editor = "Common";
            }
        }

        private static void SaveCmsMvcPartialModel(HttpContextBase httpContext, CmsPageControl control, CmsMvcPartialModel model, NameValueCollection elements)
        {
            if (elements["PartialViewName"] != null)
            {
                model.PartialViewName = elements["PartialViewName"];
            }

            model.ActionController = elements["ActionController"];
            model.ActionName = elements["ActionName"];

            if (!string.IsNullOrWhiteSpace(model.PartialModelType))
            {
                var modelType = Type.GetType(model.PartialModelType, true, true);
                object temp = Activator.CreateInstance(modelType);

                if (modelType.IsEnum || modelType.IsValueType || modelType.Equals(typeof(string)))
                {
                    if (!string.IsNullOrEmpty(elements["Model"]))
                    {
                        //note: currently there is no consistency in keeping type names...
                        //todo: it is necessary to make it consistence
                        temp = TypeInterpreter.GetValue(TypeInterpreter.GetTypeName(modelType), elements["Model"],
                                                        EntitiesConverter.ConvertToType);
                    }
                }
                else
                {
                    
                    var binder = ModelBinders.Binders.GetBinder(modelType);
                    var bindingContext = new ModelBindingContext()
                    {
                        FallbackToEmptyPrefix = true,
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, modelType),
                        ModelName = "",
                        ValueProvider = new NameValueCollectionValueProvider(elements, System.Globalization.CultureInfo.InvariantCulture)
                    };                                       
                    temp = binder.BindModel(new ControllerContext(), bindingContext);                    
                }

                control.PageControlModelValue = temp.StoreMe(modelType);
            }
            else
            {
                control.PageControlViewValue = model.StoreMe();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <returns>ViewValue, ModelValue</returns>
        public static dynamic MakeItPacked(this CmsMvcPartialModel model)
        {
            return MakeItPacked(model, null);
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="model"></param>
        /// <param name="modelValue"></param>
        /// <returns>ViewValue, ModelValue</returns>
        public static dynamic MakeItPacked(this CmsMvcPartialModel model, object modelValue)
        {
            Guard.ArgumentNotNull(model, "model");

            dynamic result = new ExpandoObject();

            result.ViewValue = model.StoreMe<CmsMvcPartialModel>();
            result.ModelValue = null;

            if (!string.IsNullOrWhiteSpace(model.PartialModelType) && (modelValue != null))
            {
                result.ModelValue = modelValue.StoreMe(Type.GetType(model.PartialModelType, true, true));
            }

            return result;
        }
    }
}
